package com.ly.mp.busicen.rule.instrumentation.plugin;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

import javax.annotation.PostConstruct;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import com.ly.mp.busicen.common.context.SwitchDbInvoke;
import com.ly.mp.busicen.rule.IRuleReload;
import com.ly.mp.busicen.rule.config.XruleConfig;
import com.ly.mp.busicen.rule.flow.ActionContainer;
import com.ly.mp.busicen.rule.flow.Fragment;
import com.ly.mp.busicen.rule.flow.IDataVolume;
import com.ly.mp.busicen.rule.flow.IFlowContext;
import com.ly.mp.busicen.rule.flow.IFlowResult;
import com.ly.mp.busicen.rule.flow.IFlowResultCtn;
import com.ly.mp.busicen.rule.flow.IFlowResultCtn.Risultato;
import com.ly.mp.busicen.rule.flow.IFlowVolume;
import com.ly.mp.busicen.rule.flow.action.ActionExecuteBase;
import com.ly.mp.busicen.rule.flow.action.IAction;
import com.ly.mp.busicen.rule.flow.action.IActionResult;
import com.ly.mp.busicen.rule.flow.engine.db.BusicenSqlMapper;
import com.ly.mp.busicen.rule.flow.filter.FlowFilter;
import com.ly.mp.busicen.rule.flow.filter.FlowInvocation;
import com.ly.mp.busicen.rule.flow.filter.FlowResult;
import com.ly.mp.busicen.rule.instrumentation.FlowInstrumentationException;
import com.ly.mp.busicen.rule.instrumentation.IFlowInstrumentation;

@Component
public class ApiDataInstrumentation implements IFlowInstrumentation, IRuleReload {
	
	Logger log = LoggerFactory.getLogger(ApiDataInstrumentation.class);
	
	List<ApiTempConf> apiConfContainer = new ArrayList<>();
	
	@Autowired
	ActionContainer actionContainer;
	
	@Autowired()
	NamedParameterJdbcTemplate jdbcTemplate;
	
	@Autowired
	XruleConfig xruleConfig;
	
	@PostConstruct
	@Override
	public boolean reload() {
		xruleConfig.xruleDataLoadCfg().pkPublishLoading().loading(this,this::load);
		return false;
	}
	
	public void load() {
		String sql = "SELECT TABLE_CODE,BUNESS_CODE,CAR_BRAND_CODE FROM "+xruleConfig.xruleDataCfg().pkPublish()+" WHERE IS_ENABLE=1";
		List<Map<String, Object>> results = jdbcTemplate.queryForList(sql,  new HashMap<String, Object>());
		if (results!=null) {
			List<ApiTempConf> temp = new ArrayList<>();
			results.forEach(m->{
				ApiTempConf atc = new ApiTempConf();
				atc.setFlow(String.valueOf(m.get("BUNESS_CODE")));
				atc.setTable(String.valueOf(m.get("TABLE_CODE")));
				atc.setBrand(String.valueOf(m.get("CAR_BRAND_CODE")));
				temp.add(atc);
			});
			apiConfContainer = temp;
		}
	}

	@Override
	public void beginFlow(String flow, String brand, Map<String, Object> data, String rid) {

	}

	@Override
	public void endBuildContext(IFlowContext context) {

	}

	@Override
	public void beginActionInvoke(IAction action, IFlowContext context, IFlowResultCtn result) {

	}

	@Override
	public void endActionInvoke(IAction action, IFlowContext context, IFlowResultCtn result, IFlowResult flowResult) {

	}

	@Override
	public void beginFilter(FlowFilter flowFilter, FlowInvocation invocation) {
		
	}
	
	@Override
	public void endFilter(FlowFilter flowFilter, FlowInvocation invocation, FlowResult filterResult) {
		
	}

	@Override
	public void beginActionExecute(IAction action, IDataVolume dataVolume, IFlowVolume flowVolume) {

	}

	@Override
	public void endActionExecute(IAction action, IDataVolume dataVolume, IFlowVolume flowVolume,
			IActionResult actionResult) {
	}

	@SuppressWarnings("unchecked")
	@Override
	public void endFlow(IFlowResultCtn result) {
		try {
			if (result.risultato() == Risultato.FINISH) {
				String flow = result.flowContext().flowVolume().flow();
				List<ApiTempConf> atcs = apiConfContainer.stream()
						.filter(m->flow.equals(m.getFlow())
								&&result.flowContext().flowVolume().actions().get(0).extention().get(IAction.EXTKEY_BRAND).equals(m.getBrand()))
						.collect(Collectors.toList());
				if (!atcs.isEmpty()) {
					Object bupk = result.flowContext().dataVolume().end().get("bupk");
					List<Object> bupks = (List<Object>) result.flowContext().dataVolume().end().get("bupks");
					if (StringUtils.isEmpty(bupk)&&(bupks==null||bupks.isEmpty())) {
						RuntimeException exception = new RuntimeException("规则引擎写入消息失败，未配置业务主键");
						throw exception;
					}
					Optional<Fragment> optional = actionContainer.getFragments().stream().filter(m->"END_BUPK_INSERT_BATCH".equals(m.getRuleCode())).findFirst();
					String sql;
					if (optional.isPresent()) {
						sql=optional.get().getContent();
					}else {
						RuntimeException exception = new RuntimeException("规则引擎写入消息失败，未配置插入消息表规则【END_BUPK_INSERT_BATCH】");
						throw exception;
					}
					
					if (!StringUtils.isEmpty(bupk)) {
						List<Object> listPk = new ArrayList<>();
						listPk.add(bupk);
						insertIntoMsgTable(flow,listPk,atcs,sql);
					}
					
					if (bupks!=null && !bupks.isEmpty()) {						
						insertIntoMsgTable(flow,bupks,atcs,sql);						
					}
					
				}
			}
		} catch (Exception e) {
			log.error("规则引擎写入消息失败，流程【{}】执行后置",result.flowContext().flowVolume().flow(),e);
			throw FlowInstrumentationException.create(e);
		}
		
	}
	
	void insertIntoMsgTable(String flow,List<Object> bupks,List<ApiTempConf> atcs,String sql) {
		List<Map<String,Object>> listData = bupks.stream().map(m->{
			Map<String,Object> param = new HashMap<>();
			param.put("bupk", m);					
			ActionExecuteBase.wrapperUserData(param);
			return param;
		}).collect(Collectors.toList());
		
		for (ApiTempConf apiTempConf : atcs) {
			log.info("流程【{}】写入消息表【{}】【{}】",flow,apiTempConf.getTable(),bupks.size());
			if(listData.size()<=500) {
				Map<String,Object> param = new HashMap<>();
				BusicenSqlMapper busicenSqlMapper = BusicenSqlMapper.create();
				param.put("table",apiTempConf.getTable());
				param.put("list", listData);
				SwitchDbInvoke.invoke("ifc", ()->busicenSqlMapper.insert(sql, param));
			}else {
				List<Map<String,Object>> batchData = new ArrayList<>();
				for (int index = 0;index<listData.size();index++) {
					batchData.add(listData.get(index));
					if(batchData.size()==500||index==(listData.size()-1)) {
						Map<String,Object> param = new HashMap<>();
						BusicenSqlMapper busicenSqlMapper = BusicenSqlMapper.create();
						param.put("table",apiTempConf.getTable());
						param.put("list", batchData);
						SwitchDbInvoke.invoke("ifc", ()->busicenSqlMapper.insert(sql, param));
						batchData.clear();
					}
				}
			}
		}
	}
	


	class ApiTempConf {

		String flow;
		String table;
		String brand;
		public String getFlow() {
			return flow;
		}
		public void setFlow(String flow) {
			this.flow = flow;
		}
		public String getTable() {
			return table;
		}
		public void setTable(String table) {
			this.table = table;
		}
		public String getBrand() {
			return brand;
		}
		public void setBrand(String brand) {
			this.brand = brand;
		}
		
	}

	@Override
	public String label() {
		return "pkasync";
	}

}
